package partFour;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/*本类用于反射的测试类*/
public class TestReflectionEnd {
    //单元测试方法：public + void + 没有参数 + @Test

    //1.通过单元测试方法，获取目标类Student对应的字节码对象
    @Test
    public void getClazz() throws ClassNotFoundException {
        //练习获取字节码对象的3中方式
        Class<?> clazz1 = Class.forName("partFour.StudentEnd");
        Class<?> clazz2 = StudentEnd.class;
        Class<?> clazz3 = new StudentEnd().getClass();

        System.out.println(clazz1);
        //class cn.review.Student  Student类对应的字节码对象
        System.out.println(clazz1.getName());
        //cn.review.Student 通过字节码对象获取到的Student的全路径名：包名+类名
        System.out.println(clazz1.getSimpleName());
        //Student 通过字节码对象获取到的Student的类名
        System.out.println(clazz2.getPackage());
        //package cn.review 通过字节码对象获取到的Student的包对象
        System.out.println(clazz2.getPackage().getName());
        //cn.review 通过包对象获取到的包名
    }
    //2.通过单元测试方法练习引用类型数组的定义与遍历
    @Test
    public void getStu(){
        //1.创建Student类的三个对象
        StudentEnd s1 = new StudentEnd("张三",3);
        StudentEnd s2 = new StudentEnd("李四",4);
        StudentEnd s3 = new StudentEnd("王麻子", 5);

        //2.创建数组将刚刚的3个对象存入数组中
        StudentEnd[] s = {s1,s2,s3};
        //3.直接打印数组，查看数组中的元素
        System.out.println(Arrays.toString(s));
        //4.遍历学生数组，拿到每一个学生对象，做进一步操作
        for(StudentEnd stu:s){
            System.out.println(stu);
            stu.play();//通过遍历到的对象执行play方法
            System.out.println(stu.age);//通过遍历到的对象，打印age属性
        }
    }
    //3.通过单元测试方法，获取Student类中的成员方法
    @Test
    public void getFunction() {
        //1.获取字节码对象
        Class<?> clazz = Student.class;

        //2.通过字节码对象，获取目标类中的成员方法们
        Method[] ms = clazz.getMethods();

        //3.通过高效for循环，拿到每一个方法对象
        for (Method m : ms) {
            System.out.println(m);//直接打印遍历到的方法对象
            System.out.println(m.getName());//通过方法对象获取方法名
            Class<?>[] pt = m.getParameterTypes();//通过方法对象获取方法所有参数的数组
            System.out.println(Arrays.toString(pt));//打印方法参数的数组
        }
    }

    //4.通过单元测试方法，获取Student类中的构造方法
    @Test
    public void getCons(){
        //1.获取字节码对象
        Class<?> clazz = new StudentEnd().getClass();
        //2.通过字节码对象获取目标类Student的构造方法们
        Constructor<?>[] cs = clazz.getConstructors();
        //3.通过高效for循环遍历数组
        for(Constructor c: cs){
            System.out.println(c.getParameterTypes());//打印本轮遍历到的构造方法的名字
            Class[] pt = c.getParameterTypes();//通过本轮遍历到的构造函数对象获取构造函数的参数类型
            System.out.println(Arrays.toString(pt));
        }
    }
    //5.通过单元测试方法，获取Student类中的成员变量
    @Test
    public void getFie() throws ClassNotFoundException{
        //1.获取字节码对象
        Class<?> clazz = Class.forName("partFour.StudentEnd");
        //2.通过字节码对象获取成员变量们
        Field[] fs = clazz.getFields();
        //3.遍历数组，获取每个成员变量的具体信息
        /*注意：目前成员变量的修饰符必须是public的才能获取到，默认修饰符也是获取不到的*/
        for(Field f : fs){
            System.out.println(f.getName());//通过本轮循环到的字段对象获取字段名
            System.out.println(f.getType());//通过本轮循环到的字段对象获取字段的类型
        }
    }
    //6.通过单元测试方法，创建Student目标类的对象
    @Test
    public void getObject() throws Exception {
        //1.获取字节码对象
        Class<?> clazz = StudentEnd.class;
        //2.通过反射技术，创建目标类的对象
        /*反射创建对象方案1：通过触发目标类的无参构造创建对象*/
        Object o = clazz.newInstance();//会抛出异常，需要throws
        System.out.println(o);//这一步已经获取大了对象Student{name='null', age=0}

        /*反射创建对象方案2：通过触发全参构造创建对象
         * 思路：1.先获取指定的构造函数对象,需要指定构造函数的参数，传入的是.class字节码对象
         *      2.通过刚刚获取到的获取到的构造函数对象，创建Student目标类的对象，并且给对象的属性赋值*/
        Constructor<?> c = clazz.getConstructor(String.class,int.class);
        //System.out.println(c);//public cn.review.Student(java.lang.String,int)
        Object o2 = c.newInstance("赵六", 6);
        System.out.println(o2);//Student{name='赵六', age=6}
    }
}